﻿

//// empty (1) 无参默认构造
//explicit set(const key_compare& comp = key_comeare(),
//				const allocator_type& alloc = allocator_type());
//// range  (2) 迭代器区间构造
//template<class InputIterator>
//	set(InputIterator first, InputIterator last,
//		const key_compare& comp = key_comeare(),
//		const allocator_type& alloc = allocator_type());
//// copy  (3) 拷贝构造
//	set(const set& x);
//
//// initializer list  (4) initializer 列表初始化构造
//	set(initializer_list<value_type> il,
//		const key_compare& comp = key_comeare(),
//		const allocator_type& alloc = allocator_type());
//
//// 迭代器是一个双向迭代器
////iterator -> a bidirectional iterator to const value_type
//
////正向迭代器
//iterator begin();
//iterator end();
//
////反向迭代器
//reverse_iterator rbegin();
//reverse_iterator rend();

////Member types  成员类型说明
////key_type	 ->		The first template parameter(T)
////value_type	 ->		The first template parameter(T)
//
////单个数据插入，如果已经存在则插入失败。
//pair<iterator, bool> insert(const value_type& val);
////列表插入，本质是将所有元素依次insert，已经在容器中存在的值不会重复插入。
//void insert(initializer_list<value_type> il);
////迭代器区间插入，已经在容器中存在的值不会插入
//template<class InputIterator>
//void insert(InputIterator first, InputIterator last);
//
////查找val，返回val所在的节点的迭代器，没有找到就返回end();
//iterator find(const value_type& val);
////查找val，返回val的个数
//size_type count(const value_type& val) const;
////删除一个迭代器位置的值
//iterator erase(const_iterator position);
////删除val，val不存在返回0，存在返回1。
//size_type erase(const value_type& val);
////删除一段迭代器区间的值
//iterator erase(const_iterator first, const_iterator last);
//
////返回大于等于val值位置的迭代器
//iterator lower_bound(const value_type& val) const;
////返回大于val值位置的迭代器
//iterator upper_bound(const value_type& val) const;


//#include<iostream>
//#include<set>
//using namespace std;
//int main()
//{
//	// 去重+升序排序
//	set<int> s;
//	// 去重+降序排序（给⼀个⼤于的仿函数）
//	//set<int, greater<int>> s;
//	s.insert(5);
//	s.insert(2);
//	s.insert(7);
//	s.insert(5);
//	//set<int>::iterator it = s.begin();
//	auto it = s.begin();
//	while (it != s.end())
//	{
//		// error C3892: “it”: 不能给常量赋值
//		// 不能修改key值
//		// *it = 1;
//		cout << *it << " ";
//		++it;
//	} 
//	cout << endl;
//	// 插⼊⼀段initializer_list列表值，已经存在的值插⼊失败
//	s.insert({ 2,8,3,9 });
//	for (auto e : s)
//	{
//		cout << e << " ";
//	} 
//	cout << endl;
//	set<string> strset = { "sort", "insert", "add" };
//	// 遍历string⽐较ascll码⼤小顺序遍历的
//	for (auto& e : strset)
//	{
//		cout << e << " ";
//	} 
//	cout << endl;
//}

//#include<iostream>
//#include<set>
//using namespace std;
//int main()
//{
//	set<int> s = { 4,2,7,2,8,5,9 };
//	for (auto e : s)
//	{
//		cout << e << " ";
//	} 
//	cout << endl;
//	// 删除最⼩值
//	s.erase(s.begin());
//	for (auto e : s)
//	{
//		cout << e << " ";
//	}
//	cout << endl;
//	// 直接删除x
//	int x;
//	cin >> x;
//	int num = s.erase(x);
//	if (num == 0)
//	{
//		cout << x << "不存在！" << endl;
//	} 
//	for (auto e : s)
//	{
//		cout << e << " ";
//	} 
//	cout << endl;
//	// 直接查找在利⽤迭代器删除x
//	cin >> x;
//	auto pos = s.find(x);
//	if (pos != s.end())
//	{
//		s.erase(pos);
//	} 
//	else
//	{
//	cout << x << "不存在！" << endl;
//	} 
//	for (auto e : s)
//	{
//		cout << e << " ";
//	} 
//	cout << endl;
//	// 算法库的查找 O(N)
//	auto pos1 = find(s.begin(), s.end(), x);
//	// set⾃⾝实现的查找 O(logN)
//	auto pos2 = s.find(x);
//	// 利⽤count间接实现快速查找
//	cin >> x;
//	if (s.count(x))
//	{
//		cout << x << "在！" << endl;
//	} 
//	else
//	{
//		cout << x << "不存在！" << endl;
//	} 
//	return 0;
//}

//#include<iostream>
//#include<set>
//using namespace std;
//int main()
//{
//	std::set<int> myset;
//	for (int i = 1; i < 10; i++)
//		myset.insert(i * 10);		// 10 20 30 40 50 60 70 80 90
//	for (auto e : myset)
//	{
//		cout << e << " ";
//	} 
//	cout << endl;
//	// 实现查找到的[itlow,itup)包含[30, 60]区间
//	// 返回 >= 30
//	auto itlow = myset.lower_bound(30);
//	// 返回 > 60
//	auto itup = myset.upper_bound(60);
//	// 删除这段区间的值
//	myset.erase(itlow, itup);
//	for (auto e : myset)
//	{
//		cout << e << " ";
//	} 
//	cout << endl;
//	return 0;
//}

//#include<iostream>
//#include<set>
//using namespace std;
//int main()
//{
//	// 相⽐set不同的是，multiset是排序，但是不去重
//	multiset<int> s = { 4,2,7,2,4,8,4,5,4,9 };
//	auto it = s.begin();
//	while (it != s.end())
//	{
//		cout << *it << " ";
//		++it;
//	} 
//	cout << endl;
//	// 相⽐set不同的是，x可能会存在多个，find查找中序的第⼀个
//	int x;
//	cin >> x;
//	auto pos = s.find(x);
//	while (pos != s.end() && *pos == x)
//	{
//		cout << *pos << " ";
//		++pos;
//	} 
//	cout << endl;
//	// 相⽐set不同的是，count会返回x的实际个数
//	cout << s.count(x) << endl;
//	// 相⽐set不同的是，erase给值时会删除所有的x
//	s.erase(x);
//	for (auto e : s)
//	{
//		cout << e << " ";
//	} 
//	cout << endl;
//	return 0;
//}



//template < class Key, // map::key_type
//	class T, // map::mapped_type
//	class Compare = less<Key>, // map::key_compare
//	class Alloc = allocator<pair<const Key, T> > //
//	map::allocator_type
//> class map;

//typedef pair<const Key, T> value_type;
//template <class T1, class T2>
//struct pair
//{
//	typedef T1 first_type;
//	typedef T2 second_type;
//	T1 first;
//	T2 second;
//	pair() : first(T1()), second(T2())
//	{}
//	pair(const T1& a, const T2& b) : first(a), second(b)
//	{}
//	template<class U, class V>
//	pair(const pair<U, V>& pr) : first(pr.first), second(pr.second)
//	{}
//};
//template <class T1, class T2>
//inline pair<T1, T2> make_pair(T1 x, T2 y)
//{
//	return (pair<T1, T2>(x, y));
//}

//
//// empty (1) ⽆参默认构造
//explicit map(const key_compare& comp = key_compare(),
//			const allocator_type& alloc = allocator_type());
//// range (2) 迭代器区间构造
//template <class InputIterator>
//map(InputIterator first, InputIterator last,
//	const key_compare& comp = key_compare(),
//	const allocator_type & = allocator_type());
//// copy (3) 拷⻉构造
//map(const map& x);
//// initializer list (5) initializer 列表构造
//map(initializer_list<value_type> il,
//	const key_compare& comp = key_compare(),
//	const allocator_type& alloc = allocator_type());
//// 迭代器是⼀个双向迭代器
////iterator -> a bidirectional iterator to const value_type
//// 正向迭代器
//iterator begin();
//iterator end();
//// 反向迭代器
//reverse_iterator rbegin();
//reverse_iterator rend();

////Member types
////key_type->The first template parameter(Key)
////mapped_type->The second template parameter(T)
////value_type->pair<const key_type, mapped_type>
//
//// 单个数据插⼊，如果已经key存在则插⼊失败,key存在相等value不相等也会插⼊失败
//pair<iterator, bool> insert(const value_type& val);
//// 列表插⼊，已经在容器中存在的值不会插⼊
//void insert(initializer_list<value_type> il);
//// 迭代器区间插⼊，已经在容器中存在的值不会插⼊
//template <class InputIterator>
//void insert(InputIterator first, InputIterator last);
//// 查找k，返回k所在的迭代器，没有找到返回end()
//iterator find(const key_type& k);
//// 查找k，返回k的个数
//size_type count(const key_type& k) const;
//// 删除⼀个迭代器位置的值
//iterator erase(const_iterator position);
//// 删除k，k存在返回0，存在返回1
//size_type erase(const key_type& k);
//// 删除⼀段迭代器区间的值
//iterator erase(const_iterator first, const_iterator last);

////Member types
////key_type -> The first template parameter(Key)
////mapped_type -> The second template parameter(T)
////value_type -> pair<const key_type, mapped_type>
//
//
//iterator find(const key_type& k);
//// 查找k，返回k所在的迭代器，没有找到返回end()，
//// 如果找到了通过iterator可以修改key对应的 mapped_type 值
//
//pair<iterator, bool> insert(const value_type& val);
//// ⽂档中对insert返回值的说明
//// insert插⼊⼀个pair<key, T>对象：
//// 1、如果key已经在map中，插⼊失败，则返回⼀个pair<iterator,bool>对象，返回pair对象
////first是key所在结点的迭代器，second是false
//// 2、如果key不在在map中，插⼊成功，则返回⼀个pair<iterator,bool>对象，返回pair对象
////first是新插⼊key所在结点的迭代器，second是true
//// 也就是说⽆论插⼊成功还是失败，返回pair<iterator,bool>对象的first都会指向key所在的迭代器
//// 那么也就意味着insert插⼊失败时充当了查找的功能，正是因为这⼀点，insert可以⽤来实现operator[]
//// 需要注意的是这里有两个pair，不要混淆了，⼀个是map底层红⿊树节点中存的pair<key, T>，
////另⼀个是insert返回值pair<iterator, bool>。
//
//mapped_type& operator[] (const key_type& k);
//// operator的内部实现
//mapped_type& operator[] (const key_type& k)
//{
//	// 1、如果k不在map中，insert会插⼊k和mapped_type默认值，同时[]返回结点中存储
//	//mapped_type值的引⽤，那么我们可以通过引⽤修改返映射值。所以[]具备了插⼊ + 修改功能
//	// 2、如果k在map中，insert会插⼊失败，但是insert返回pair对象的first是指向key结点的
//	//迭代器，返回值同时[]返回结点中存储mapped_type值的引⽤，所以[]具备了查找 + 修改的功能
//	pair<iterator, bool> ret = insert({ k, mapped_type() });
//	iterator it = ret.first;
//	return it->second;
//}


//#include<iostream>
//#include<map>
//using namespace std;
//int main()
//{
//	// initializer_list构造及迭代遍历
//	map<string, string> dict = { {"left", "左边"}, {"right", "右边"}, {"insert", "插⼊"}, { "string", "字符串" } };
//	//map<string, string>::iterator it = dict.begin();
//	auto it = dict.begin();
//	while (it != dict.end())
//	{
//		cout << (*it).first <<":"<<(*it).second << endl;		//解引用后引用pair内的值
//		
//		// map的迭代基本都使⽤operator->,这⾥省略了⼀个->
//		// 第⼀个->是迭代器运算符重载，返回pair*，第⼆个箭头是结构指针解引⽤取pair数据
//		//cout << it.operator->()->first << ":" << it.operator->()-> second << endl;
//		cout << it->first << ":" << it->second << endl;	//简化写法
//		++it;
//	} 
//	cout << endl;
//	// insert插⼊pair对象的4种⽅式，对比之下，最后⼀种最⽅便
//	//1、构造之后插入
//	pair<string, string> kv1("first", "第⼀个");
//	dict.insert(kv1);
//	//2、使用pair匿名对象构造一个键值对插入
//	dict.insert(pair<string, string>("second", "第⼆个"));
//	//3、使用make_pair返回一个构造好的键值对
//	dict.insert(make_pair("sort", "排序"));
//	//4、C++11：initializer初始化列表->多参数构造支持隐式类型转换
//	dict.insert({ "auto", "⾃动的" });
//
//	// "left"已经存在，插⼊失败
//	dict.insert({ "left", "左边，剩余" });
//
//	// 范围for遍历
//	for (const auto& e : dict)
//	{
//		cout << e.first << ":" << e.second << endl;
//	} 
//	cout << endl;
//
//	string str;
//	while (cin >> str)
//	{
//		auto ret = dict.find(str);
//		if (ret != dict.end())
//		{
//			cout << "->" << ret->second << endl;
//		} 
//		else
//		{
//		cout << "⽆此单词，请重新输⼊" << endl;
//		}
//	} 
//	// erase等接⼝跟set完全类似，这⾥就不演⽰讲解了
//	return 0;
//}


//#include<iostream>
//#include<map>
//#include<string>
//using namespace std;
//int main()
//{
//	// 利⽤find和iterator修改功能，统计⽔果出现的次数
//	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉" };
//	map<string, int> countMap;
//	for (const auto& str : arr)
//	{
//		// 先查找⽔果在不在map中
//		// 1、不在，说明⽔果第⼀次出现，则插⼊{⽔果, 1}
//		// 2、在，则查找到的节点中⽔果对应的次数++
//		auto ret = countMap.find(str);
//		if (ret == countMap.end())
//		{
//			countMap.insert({ str, 1 });
//		} 
//		else
//		{
//		ret->second++;
//		}
//	} 
//	for (const auto & e : countMap)
//	{
//		cout << e.first << ":" << e.second << endl;
//	} 
//	cout << endl;
//	return 0;
//}

//#include<iostream>
//#include<map>
//#include<string>
//using namespace std;
//int main()
//{
//	// 利⽤[]插⼊+修改功能，巧妙实现统计⽔果出现的次数
//	string arr[] = { "苹果", "西⽠", "苹果", "西⽠", "苹果", "苹果", "西⽠","苹果", "⾹蕉", "苹果", "⾹蕉" };
//	map<string, int> countMap;
//	for (const auto& str : arr)
//	{
//		// []先查找⽔果在不在map中
//		// 1、不在，说明⽔果第⼀次出现，则插⼊{⽔果, 0}，同时返回次数的引⽤，++⼀下就变成1次了
//		// 2、在，则返回⽔果对应的次数++
//		countMap[str]++;
//	} 
//	for (const auto & e : countMap)
//	{
//		cout << e.first << ":" << e.second << endl;
//	} 
//	cout << endl;
//	return 0;
//}


//#include<iostream>
//#include<map>
//#include<string>
//using namespace std;
//int main()
//{
//	// 利⽤[]插⼊+修改功能，巧妙实现统计⽔果出现的次数
//	string arr[] = { "苹果", "西⽠", "苹果", "西⽠", "苹果", "苹果", "西⽠","苹果", "⾹蕉", "苹果", "⾹蕉" };
//	map<string, int> countMap;
//	for (const auto& str : arr)
//	{
//		// []先查找⽔果在不在map中
//		// 1、不在，说明⽔果第⼀次出现，则插⼊{⽔果, 0}，同时返回次数的引⽤，++⼀下就变成1次了
//		// 2、在，则返回⽔果对应的次数++
//		countMap[str]++;
//	}
//	for (const auto& e : countMap)
//	{
//		cout << e.first << ":" << e.second << endl;
//	}
//	cout << endl;
//	return 0;
//}

//int main()
//{
//	map<string, string> dict;
//	dict.insert(make_pair("sort", "排序"));
//
//	// key不存在->插⼊ {"insert", string()}
//	dict["insert"];
//
//	// key不存在->插⼊+修改
//	dict["left"] = "左边";
//
//	// key存在->修改
//	dict["left"] = "左边、剩余";
//
//	// key存在->查找
//	cout << dict["left"] << endl;
//
//	return 0;
//}

//


#include <iostream>
#include<map>
#include<string>
#include <vector>
#include<algorithm>
using namespace std;

class compare
{
public:
    bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2)
    {
        return kv1.second > kv2.second || (kv1.second == kv2.second && kv1.first < kv1.first);
    }
};


int main()
{
    map<string, int> m;
    string s;
    getline(cin, s);
    for (int i = 0, j = 0; i < s.size(); i++)
    {
        if (s[i] == ' ' || s[i] == '.')
        {
            string t = s.substr(j, i - j);
            if (isupper(t[0]))
                t[0] = tolower(t[0]);
            j = i + 1;
            m[t]++;
        }
    }

    vector<pair<string, int>> strV(m.begin(), m.end());
    sort(strV.begin(), strV.end(), compare());
    for (int i = 0; i < strV.size(); i++)
        cout << strV[i].first << ":" << strV[i].second << endl;


}
